#include "ExpressionCalcul.hpp"

#include <cmath>

#include "ProgramManager.hpp"
#include "CallingInstance.hpp"
#include "Memory.hpp"

bool
isNumerical ( const ExpressionValue& op )
{
    return ( op._type._modifier == NONE && ( op._type._base == INTEGER || op._type._base == REAL ) );
}

float
getNumerical ( const ExpressionValue& op )
{
    CallingInstance* instance;
    Memory* memoire;
    int vtmp;
    float res = 0.0f;
    if ( op._type._modifier == NONE )
    {
        switch ( op._class )
        {
        case CE_VALUE:
            if ( op._type._base == INTEGER )
            {
                res = (float) op._value._vinteger;
            }
            else if ( op._type._base == REAL )
            {
                res = op._value._vreal;
            }
            break;
        case CE_MEMORY:
            instance = ProgramManager::getCurrentInstance ();
            memoire = instance->getMemory ();
            if ( op._type._base == INTEGER )
            {
                memoire->get ( op._value._vaddress, (void*) & vtmp, getTypeSize ( op._type ) );
                res = (float) vtmp;
            }
            else if ( op._type._base == REAL )
            {
                memoire->get ( op._value._vaddress, (void*) & res, getTypeSize ( op._type ) );
            }
            break;
        default:
            break;
        }
    }
    return res;
}

void
setNumerical ( ExpressionValue& op, float value )
{
    CallingInstance* instance;
    Memory* memoire;
    int vtmp;
    if ( op._type._modifier == NONE )
    {
        switch ( op._class )
        {
        case CE_VALUE:
            if ( op._type._base == INTEGER )
                op._value._vinteger = (int) floorf ( value );
            else if ( op._type._base == REAL )
                op._value._vreal = value;
            break;
        case CE_MEMORY:
            instance = ProgramManager::getCurrentInstance ();
            memoire = instance->getMemory ();
            if ( op._type._base == INTEGER )
            {
                vtmp = (int) value;
                memoire->set ( op._value._vaddress, (void*) & vtmp, getTypeSize ( op._type ) );
            }
            else if ( op._type._base == REAL )
            {
                memoire->set ( op._value._vaddress, (void*) & value, getTypeSize ( op._type ) );
            }
            break;
        default:
            break;
        }
    }
}

bool
isBoolean ( const ExpressionValue& op )
{
    return ( op._type._modifier == NONE && ( op._type._base == INTEGER || op._type._base == BOOLEAN ) );
}

bool
getBoolean ( const ExpressionValue& op )
{
    if ( op._type._modifier == NONE )
    {
        if ( op._type._base == INTEGER )
            return (bool) op._value._vinteger;
        else if ( op._type._base == BOOLEAN )
            return op._value._vboolean;
        else
            return false;
    }
    else
        return false;
}

void
setBoolean ( ExpressionValue& op, bool value )
{
    if ( op._type._modifier == NONE )
    {
        if ( op._type._base == INTEGER )
            op._value._vinteger = value ? 1 : 0;
        else if ( op._type._base == BOOLEAN )
            op._value._vboolean = value;
    }
}

ExpressionValue
operator== ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = BOOLEAN;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isNumerical ( op1 ) && isNumerical ( op2 ) )
    {
        result._value._vboolean = getNumerical ( op1 ) == getNumerical ( op2 );
    }
    else if ( isBoolean ( op1 ) && isBoolean ( op2 ) )
    {
        result._value._vboolean = getBoolean ( op1 ) == getBoolean ( op2 );
    }
    else if ( op1._type._modifier == POINTER && op2._type._modifier == POINTER )
    {
        result._value._vboolean = op1._value._vaddress == op2._value._vaddress;
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator!= ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = BOOLEAN;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isNumerical ( op1 ) && isNumerical ( op2 ) )
    {
        result._value._vboolean = getNumerical ( op1 ) != getNumerical ( op2 );
    }
    else if ( isBoolean ( op1 ) && isBoolean ( op2 ) )
    {
        result._value._vboolean = getBoolean ( op1 ) != getBoolean ( op2 );
    }
    else if ( op1._type._modifier == POINTER && op2._type._modifier == POINTER )
    {
        result._value._vboolean = op1._value._vaddress != op2._value._vaddress;
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator< ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = BOOLEAN;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isNumerical ( op1 ) && isNumerical ( op2 ) )
    {
        result._value._vboolean = getNumerical ( op1 ) < getNumerical ( op2 );
    }
    else if ( op1._type._modifier == POINTER && op2._type._modifier == POINTER )
    {
        result._value._vboolean = op1._value._vaddress < op2._value._vaddress;
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator<= ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = BOOLEAN;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isNumerical ( op1 ) && isNumerical ( op2 ) )
    {
        result._value._vboolean = getNumerical ( op1 ) <= getNumerical ( op2 );
    }
    else if ( op1._type._modifier == POINTER && op2._type._modifier == POINTER )
    {
        result._value._vboolean = op1._value._vaddress <= op2._value._vaddress;
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator> ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = BOOLEAN;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isNumerical ( op1 ) && isNumerical ( op2 ) )
    {
        result._value._vboolean = getNumerical ( op1 ) > getNumerical ( op2 );
    }
    else if ( op1._type._modifier == POINTER && op2._type._modifier == POINTER )
    {
        result._value._vboolean = op1._value._vaddress > op2._value._vaddress;
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator>= ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = BOOLEAN;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isNumerical ( op1 ) && isNumerical ( op2 ) )
    {
        result._value._vboolean = getNumerical ( op1 ) >= getNumerical ( op2 );
    }
    else if ( op1._type._modifier == POINTER && op2._type._modifier == POINTER )
    {
        result._value._vboolean = op1._value._vaddress >= op2._value._vaddress;
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator+ ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = TYPE_ERROR;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isNumerical ( op1 ) && isNumerical ( op2 ) )
    {
        result._type._base = op1._type._base == REAL || op2._type._base == REAL ? REAL : INTEGER;
        setNumerical ( result, getNumerical ( op1 ) + getNumerical ( op2 ) );
    }
    else if ( op1._type._modifier == POINTER && ( op2._type._modifier == NONE && op2._type._base == INTEGER ) )
    {
        result._type = op1._type;
        result._value._vaddress = op1._value._vaddress + op2._value._vinteger;
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator- ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = TYPE_ERROR;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isNumerical ( op1 ) && isNumerical ( op2 ) )
    {
        result._type._base = op1._type._base == REAL || op2._type._base == REAL ? REAL : INTEGER;
        setNumerical ( result, getNumerical ( op1 ) - getNumerical ( op2 ) );
    }
    else if ( op1._type._modifier == POINTER && ( op2._type._modifier == NONE && op2._type._base == INTEGER ) )
    {
        result._type = op1._type;
        result._value._vaddress = op1._value._vaddress - op2._value._vinteger;
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator* ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = TYPE_ERROR;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isNumerical ( op1 ) && isNumerical ( op2 ) )
    {
        result._type._base = op1._type._base == REAL || op2._type._base == REAL ? REAL : INTEGER;
        setNumerical ( result, getNumerical ( op1 ) * getNumerical ( op2 ) );
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator/ ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = TYPE_ERROR;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isNumerical ( op1 ) && isNumerical ( op2 ) )
    {
        result._type._base = REAL;
        setNumerical ( result, getNumerical ( op1 ) / getNumerical ( op2 ) );
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator&& ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = TYPE_ERROR;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isBoolean ( op1 ) && isBoolean ( op2 ) )
    {
        result._type._base = BOOLEAN;
        setBoolean ( result, getBoolean ( op1 ) && getBoolean ( op2 ) );
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}

ExpressionValue
operator|| ( const ExpressionValue& op1, const ExpressionValue& op2 )
{
    ExpressionValue result;
    result._class = CE_VALUE;
    result._type._base = TYPE_ERROR;
    result._type._modifier = NONE;
    result._type._size = 0;
    result._value._vboolean = true;
    if ( isBoolean ( op1 ) && isBoolean ( op2 ) )
    {
        result._type._base = BOOLEAN;
        setBoolean ( result, getBoolean ( op1 ) || getBoolean ( op2 ) );
    }
    else
    {
        result._type._base = TYPE_ERROR;
    }
    return result;
}
